Conversation
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
|
@iNecas: This pull request references OLS-2722 which is a valid jira issue. DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository. |
src/hooks/useToolUIMapping.ts
Outdated
| import type { OlsToolUIComponent } from '../types'; | ||
|
|
||
| type ToolUIExtensionProperties = { | ||
| /** Id of the component (as refferrenced by the mcp tool) */ |
There was a problem hiding this comment.
| /** Id of the component (as refferrenced by the mcp tool) */ | |
| /** ID of the component (as referenced by the MCP tool) */ |
src/components/OlsToolUIs.tsx
Outdated
| export const OlsToolUI: React.FC<OlsToolUIProps> = ({ tool, toolUIComponent: toolUIElement }) => { | ||
| const ToolComponent = toolUIElement; | ||
| return <ToolComponent tool={tool} />; | ||
| }; |
There was a problem hiding this comment.
| export const OlsToolUI: React.FC<OlsToolUIProps> = ({ tool, toolUIComponent: toolUIElement }) => { | |
| const ToolComponent = toolUIElement; | |
| return <ToolComponent tool={tool} />; | |
| }; | |
| export const OlsToolUI: React.FC<OlsToolUIProps> = ({ tool, toolUIComponent: ToolComponent }) => { | |
| return <ToolComponent tool={tool} />; | |
| }; |
|
Also, we should add a brief description of how to use this extension to the README |
|
PR needs rebase. DetailsInstructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. |
WalkthroughAdds runtime support for rendering external plugin-provided tool UI components: new hook to map plugin extensions, new components to render mapped UIs per chat entry, a small event-payload change to propagate the UI id, and corresponding type updates and docs. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
I've rebased and fixed an issue where we were visualizing the tool calls in failed state as well. I will proceed with final cleanup and will switch to non-draft state for final review after that. |
|
@iNecas Probably also a good idea to wrap the OlsToolUIs in an |
Allows plugins to define 'ols.tool-ui' extensions to map the tools
to react components to be rendered when the tool gets called.
Sample extensions registration:
```json
{
"type": "ols.tool-ui",
"properties": {
"id": "my-obs/my-tool",
"component": {
"$codeRef": "MyToolUI"
}
}
}
```
The ToolUI implemntation receives the tool details in it's argument:
```ts
type MyTool = {
name: 'my-tool';
args: object,
// ...
};
export const MyToolUI React.FC<{ tool: MyTool }> = ({ tool }) => {
// component implementation
}
```
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/components/OlsToolUIs.tsx`:
- Around line 27-37: toolsData can be undefined causing olsToolsWithUI to throw
when calling .map(); guard by converting undefined to an empty Immutable Map or
early-return before mapping: in the selector call/assignment for toolsData
(where useSelector retrieves s.plugins?.ols?.getIn(['chatHistory', entryIndex,
'tools'])), ensure toolsData is set to an empty Immutable.Map() when falsy, or
check toolsData before creating olsToolsWithUI; then run .map()/.filter only on
a guaranteed Immutable collection so olsToolsWithUI, toolUIMapping, and
downstream code using tool and toolUIComponent are safe.
In `@src/types.ts`:
- Line 38: The olsToolUiID property in the type definition is currently required
but is treated as optional in usage (see olsToolUiID references in Prompt.tsx
and OlsToolUIs.tsx and the fact it may arrive only with tool_result events), so
update the type that declares olsToolUiID to make it optional (e.g., change
olsToolUiID: string to olsToolUiID?: string) so the compiler reflects actual
runtime behavior and avoids false positives in places that guard against its
absence; ensure you update the interface or type in src/types.ts where
olsToolUiID is declared and run type checks to verify callers (Prompt.tsx,
OlsToolUIs.tsx, any tool_call/tool_result handlers) still compile.
- Line 31: The args field in the shared type is declared as "{ [key: string]:
string }" but the codebase uses it as a generic map (ResponseToolModal.tsx casts
to Record<string, unknown> and MCPApp.tsx's handleToolCall expects
Record<string, unknown>). Update the type definition for args to Record<string,
unknown> in the declaration (the symbol to change is the args field in the type
in src/types.ts) so it matches the actual usage and avoids unsafe casts.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 373c36eb-a1a5-4c6e-8c57-072ed2d2d095
📒 Files selected for processing (5)
src/components/OlsToolUIs.tsxsrc/components/Prompt.tsxsrc/components/ResponseTools.tsxsrc/hooks/useToolUIMapping.tssrc/types.ts
| const toolsData: ImmutableMap<string, ImmutableMap<string, unknown>> = useSelector((s: State) => | ||
| s.plugins?.ols?.getIn(['chatHistory', entryIndex, 'tools']), | ||
| ); | ||
|
|
||
| const olsToolsWithUI = toolsData | ||
| .map((value) => { | ||
| const tool = value.toJS() as Tool; | ||
| const toolUIComponent = tool.olsToolUiID && toolUIMapping[tool.olsToolUiID]; | ||
| return { tool, toolUIComponent }; | ||
| }) | ||
| .filter(({ tool, toolUIComponent }) => tool.status !== 'error' && !!toolUIComponent); |
There was a problem hiding this comment.
Add null guard for toolsData to prevent runtime errors.
If entryIndex is invalid or the chat entry doesn't have tools yet, getIn returns undefined. Calling .map() on undefined will throw a runtime error.
Proposed fix
const toolsData: ImmutableMap<string, ImmutableMap<string, unknown>> = useSelector((s: State) =>
s.plugins?.ols?.getIn(['chatHistory', entryIndex, 'tools']),
);
+ if (!toolsData) {
+ return null;
+ }
+
const olsToolsWithUI = toolsData
.map((value) => {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const toolsData: ImmutableMap<string, ImmutableMap<string, unknown>> = useSelector((s: State) => | |
| s.plugins?.ols?.getIn(['chatHistory', entryIndex, 'tools']), | |
| ); | |
| const olsToolsWithUI = toolsData | |
| .map((value) => { | |
| const tool = value.toJS() as Tool; | |
| const toolUIComponent = tool.olsToolUiID && toolUIMapping[tool.olsToolUiID]; | |
| return { tool, toolUIComponent }; | |
| }) | |
| .filter(({ tool, toolUIComponent }) => tool.status !== 'error' && !!toolUIComponent); | |
| const toolsData: ImmutableMap<string, ImmutableMap<string, unknown>> = useSelector((s: State) => | |
| s.plugins?.ols?.getIn(['chatHistory', entryIndex, 'tools']), | |
| ); | |
| if (!toolsData) { | |
| return null; | |
| } | |
| const olsToolsWithUI = toolsData | |
| .map((value) => { | |
| const tool = value.toJS() as Tool; | |
| const toolUIComponent = tool.olsToolUiID && toolUIMapping[tool.olsToolUiID]; | |
| return { tool, toolUIComponent }; | |
| }) | |
| .filter(({ tool, toolUIComponent }) => tool.status !== 'error' && !!toolUIComponent); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/components/OlsToolUIs.tsx` around lines 27 - 37, toolsData can be
undefined causing olsToolsWithUI to throw when calling .map(); guard by
converting undefined to an empty Immutable Map or early-return before mapping:
in the selector call/assignment for toolsData (where useSelector retrieves
s.plugins?.ols?.getIn(['chatHistory', entryIndex, 'tools'])), ensure toolsData
is set to an empty Immutable.Map() when falsy, or check toolsData before
creating olsToolsWithUI; then run .map()/.filter only on a guaranteed Immutable
collection so olsToolsWithUI, toolUIMapping, and downstream code using tool and
toolUIComponent are safe.
|
|
||
| export type Tool = { | ||
| args: { [key: string]: Array<string> }; | ||
| args: { [key: string]: string }; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for existing usages of tool.args to verify the expected structure
rg -n "tool\.args|args\[" --type=ts -C3Repository: openshift/lightspeed-console
Length of output: 54
🏁 Script executed:
cat -n src/types.ts | head -50Repository: openshift/lightspeed-console
Length of output: 1392
🏁 Script executed:
rg -n "args" src/types.ts -B2 -A2Repository: openshift/lightspeed-console
Length of output: 178
🏁 Script executed:
# Search more broadly for how args is used in the codebase
rg "\.args" --type=ts -i | head -20Repository: openshift/lightspeed-console
Length of output: 529
🏁 Script executed:
# Search for API response examples or test data that might show the actual structure
fd -e "test\|spec\|mock" -t f | head -10Repository: openshift/lightspeed-console
Length of output: 54
🏁 Script executed:
# Search for Tool type usage
rg "tool\.args|Tool\>" --type=ts -A2 -B2 | head -40Repository: openshift/lightspeed-console
Length of output: 170
🏁 Script executed:
# Look for streaming or API-related code
fd -e ts -e tsx | xargs rg "stream|api|tool_call" -l | head -10Repository: openshift/lightspeed-console
Length of output: 319
🏁 Script executed:
# Check git history to see what the type changed from
git log --oneline -p src/types.ts | head -100Repository: openshift/lightspeed-console
Length of output: 1818
🏁 Script executed:
rg "tool\.args" src/components/ -B3 -A3Repository: openshift/lightspeed-console
Length of output: 54
🏁 Script executed:
rg "args" src/attachments.ts -B2 -A2Repository: openshift/lightspeed-console
Length of output: 54
🏁 Script executed:
# Check if Tool type existed in a different location before
git log --all --full-history -S "type Tool" --onelineRepository: openshift/lightspeed-console
Length of output: 158
🏁 Script executed:
cat src/components/Prompt.tsx | grep -n "tool" -i -C3 | head -50Repository: openshift/lightspeed-console
Length of output: 1415
🏁 Script executed:
cat src/components/AttachEventsModal.tsx | grep -n "tool\|args" -i -C2 | head -40Repository: openshift/lightspeed-console
Length of output: 54
🏁 Script executed:
# Search for any access to Tool properties
rg "tool\[|tool\." src/ --type=ts --type=tsx -C2Repository: openshift/lightspeed-console
Length of output: 99
🏁 Script executed:
rg "tool\[|tool\." src/ -C2 | head -80Repository: openshift/lightspeed-console
Length of output: 1975
🏁 Script executed:
# Check what the review is referring to - search for Array<string> type references
rg "Array<string>" src/types.tsRepository: openshift/lightspeed-console
Length of output: 54
🏁 Script executed:
# Look at the full context of how Tool type is used in components
fd -e ts -e tsx -path "*/src/components/*" | xargs cat | grep -A5 -B5 "Tool"Repository: openshift/lightspeed-console
Length of output: 240
🏁 Script executed:
# Check if there's any documentation or API reference
fd -e md | xargs cat | grep -i "args\|tool" | head -20Repository: openshift/lightspeed-console
Length of output: 54
🏁 Script executed:
rg "args.*Record|args.*unknown" src/ -B2 -A2Repository: openshift/lightspeed-console
Length of output: 811
🏁 Script executed:
# Check all Tool property usages to see if args is accessed directly anywhere
rg "\.args\[|\.args\." src/ -C3Repository: openshift/lightspeed-console
Length of output: 54
Fix args type to match actual usage patterns.
The args field type is defined as { [key: string]: string }, but the codebase expects it to hold values of any type. In ResponseToolModal.tsx, the code casts args as Record<string, unknown>, and in MCPApp.tsx, handleToolCall expects args: Record<string, unknown>. The type definition should be changed to Record<string, unknown> to reflect the actual API contract and prevent type safety issues.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/types.ts` at line 31, The args field in the shared type is declared as "{
[key: string]: string }" but the codebase uses it as a generic map
(ResponseToolModal.tsx casts to Record<string, unknown> and MCPApp.tsx's
handleToolCall expects Record<string, unknown>). Update the type definition for
args to Record<string, unknown> in the declaration (the symbol to change is the
args field in the type in src/types.ts) so it matches the actual usage and
avoids unsafe casts.
| uiResourceUri?: string; | ||
| serverName?: string; | ||
| structuredContent?: Record<string, unknown>; | ||
| olsToolUiID: string; |
There was a problem hiding this comment.
olsToolUiID should be optional to match actual usage.
The olsToolUiID field is defined as required, but the implementation shows it's conditionally populated:
- In
Prompt.tsx(line 634), it's only included whentoolMeta?.olsUi?.idis present - In
OlsToolUIs.tsx(line 34), there's a guardtool.olsToolUiID && toolUIMapping[...]suggesting it can be falsy - Tool entries are created incrementally via
tool_callandtool_resultevents, whereolsToolUiIDonly arrives withtool_result
Proposed fix
- olsToolUiID: string;
+ olsToolUiID?: string;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| olsToolUiID: string; | |
| olsToolUiID?: string; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/types.ts` at line 38, The olsToolUiID property in the type definition is
currently required but is treated as optional in usage (see olsToolUiID
references in Prompt.tsx and OlsToolUIs.tsx and the fact it may arrive only with
tool_result events), so update the type that declares olsToolUiID to make it
optional (e.g., change olsToolUiID: string to olsToolUiID?: string) so the
compiler reflects actual runtime behavior and avoids false positives in places
that guard against its absence; ensure you update the interface or type in
src/types.ts where olsToolUiID is declared and run type checks to verify callers
(Prompt.tsx, OlsToolUIs.tsx, any tool_call/tool_result handlers) still compile.
|
Addressed all review comments. Planning to finish the coderabbit ones + any other comments on Monday. |
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
src/components/OlsToolUIs.tsx (1)
27-31:⚠️ Potential issue | 🔴 CriticalGuard
toolsDatabefore calling.map()to prevent a render crash.At Line 31,
.map()executes even when selector output isundefined(e.g., missing tools or invalidentryIndex), which throws at runtime.Proposed fix
- const toolsData: ImmutableMap<string, ImmutableMap<string, unknown>> = useSelector((s: State) => - s.plugins?.ols?.getIn(['chatHistory', entryIndex, 'tools']), - ); + const toolsData = useSelector( + (s: State): ImmutableMap<string, ImmutableMap<string, unknown>> | undefined => + s.plugins?.ols?.getIn(['chatHistory', entryIndex, 'tools']), + ); + + if (!toolsData) { + return null; + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/OlsToolUIs.tsx` around lines 27 - 31, toolsData (from the useSelector that reads s.plugins?.ols?.getIn(['chatHistory', entryIndex, 'tools'])) can be undefined, and calling .map() on it when constructing olsToolsWithUI causes a render crash; guard before mapping by checking toolsData is defined (and is an Immutable Map/has size) and only call .map() when present, otherwise set olsToolsWithUI to an empty array or Immutable List; update the olsToolsWithUI assignment to conditionally map (e.g., if (toolsData) { toolsData.map(...) } else fallback) so rendering is safe when entryIndex/tools are missing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@README.md`:
- Around line 267-291: The README shows mismatched IDs between the MCP
annotation (_meta.additionalFields.olsUi.id set to 'my-mcp/my-tool') and the
plugin extension (the "ols.tool-ui" extension's properties.id set to
'my-obs/my-tool'); update one so both use the exact same tool UI id (e.g., make
both 'my-mcp/my-tool' or both 'my-obs/my-tool') so the mapping resolves; verify
the id in the annotation and the "ols.tool-ui" extension's properties.id match
exactly and keep the component.$codeRef (MyToolUI) unchanged.
- Around line 299-307: The example TypeScript component declaration is missing
the colon between the component name and its type annotation; update the
declaration for MyToolUI so the identifier MyToolUI is followed by a colon and
the React.FC type annotation (i.e., change the declaration with MyToolUI
React.FC<{ tool: MyTool }> to use a colon between MyToolUI and React.FC) so the
example compiles as valid TypeScript.
---
Duplicate comments:
In `@src/components/OlsToolUIs.tsx`:
- Around line 27-31: toolsData (from the useSelector that reads
s.plugins?.ols?.getIn(['chatHistory', entryIndex, 'tools'])) can be undefined,
and calling .map() on it when constructing olsToolsWithUI causes a render crash;
guard before mapping by checking toolsData is defined (and is an Immutable
Map/has size) and only call .map() when present, otherwise set olsToolsWithUI to
an empty array or Immutable List; update the olsToolsWithUI assignment to
conditionally map (e.g., if (toolsData) { toolsData.map(...) } else fallback) so
rendering is safe when entryIndex/tools are missing.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 4967151b-df62-49f6-a9e6-e8b1651b72bf
📒 Files selected for processing (3)
README.mdsrc/components/OlsToolUIs.tsxsrc/hooks/useToolUIMapping.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- src/hooks/useToolUIMapping.ts
| 1. annotate the particular MCP tool inside the MCP server: | ||
|
|
||
| ``` json | ||
| _meta: { | ||
| additionalFields: { | ||
| olsUi: { | ||
| id: 'my-mcp/my-tool', | ||
| }, | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| 2. define an extension of type `ols.tool-ui` inside the plugin, connecting the tool (using the annotated id) | ||
| with the particular component: | ||
|
|
||
| ``` json | ||
| { | ||
| "type": "ols.tool-ui", | ||
| "properties": { | ||
| "id": "my-obs/my-tool", | ||
| "component": { | ||
| "$codeRef": "MyToolUI" | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Use the same tool UI id in both setup steps.
Line 273 (my-mcp/my-tool) and Line 286 (my-obs/my-tool) differ, but the mapping requires an exact id match. With this mismatch, the Tool UI will not resolve.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@README.md` around lines 267 - 291, The README shows mismatched IDs between
the MCP annotation (_meta.additionalFields.olsUi.id set to 'my-mcp/my-tool') and
the plugin extension (the "ols.tool-ui" extension's properties.id set to
'my-obs/my-tool'); update one so both use the exact same tool UI id (e.g., make
both 'my-mcp/my-tool' or both 'my-obs/my-tool') so the mapping resolves; verify
the id in the annotation and the "ols.tool-ui" extension's properties.id match
exactly and keep the component.$codeRef (MyToolUI) unchanged.
| ``` typescript | ||
| type MyTool = { | ||
| name: 'my-tool'; | ||
| args: object, | ||
| // ... | ||
| }; | ||
|
|
||
| export const MyToolUI React.FC<{ tool: MyTool }> = ({ tool }) => { | ||
| // component implementation |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Expect 1 match before fix, 0 matches after fix
rg -n -C2 'export const MyToolUI React\.FC<\{ tool: MyTool \}>' README.mdRepository: openshift/lightspeed-console
Length of output: 194
Add missing colon in TypeScript component declaration.
Line 306 is missing the : between MyToolUI and React.FC, making the example invalid TypeScript that won't compile when copied.
Proposed fix
-export const MyToolUI React.FC<{ tool: MyTool }> = ({ tool }) => {
+export const MyToolUI: React.FC<{ tool: MyTool }> = ({ tool }) => {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@README.md` around lines 299 - 307, The example TypeScript component
declaration is missing the colon between the component name and its type
annotation; update the declaration for MyToolUI so the identifier MyToolUI is
followed by a colon and the React.FC type annotation (i.e., change the
declaration with MyToolUI React.FC<{ tool: MyTool }> to use a colon between
MyToolUI and React.FC) so the example compiles as valid TypeScript.

Allows plugins to define 'ols.tool-ui' extensions to map the tools to react components to be rendered when the tool gets called.
Sample extensions registration:
{ "type": "ols.tool-ui", "properties": { "id": "my-obs/my-tool", "component": { "$codeRef": "MyToolUI" } } }The ToolUI implemntation receives the tool details in it's argument:
Depends on openshift/lightspeed-service#2779
This allows monitoring-plugin to define a UI for obs-mcp tools

Related changes leveraging this feature: